#ifndef CPPPRIMER5THCHAPTER16
#define CPPPRIMER5THCHAPTER16
#include <cstdlib>
#include <string>
#include <vector>
#include <list>
#include <iostream>
#include <fstream>
#include <sstream>
#include <cctype>
#include <utility>
#include <functional>
#include <cstring>
#include <memory>
#include <set>
#include <iterator>
#include <initializer_list>
#include <cstddef>
#include <stdexcept>
#include <algorithm>
#include <type_traits>
#include <climits>
#include <unordered_set>
#include <boost/iterator/reverse_iterator.hpp>

#include "../chapter7/chapter7.h"
#include "../chapter13/chapter13.h"

using std::cerr;
using std::cin;
using std::cout;
using std::endl;
using std::istream;
using std::ostream;
// #define NDEBUG
#include "../../hello_log.h"

template <typename T>
int rangeForContainer(T &a)
{
    for (auto temp = a.begin(); temp != a.end(); temp++)
    {
        cout << *temp << ",";
    }
    cout << endl;
    return 0;
}

template <typename T>
int rangeForContainerUseIndex(T &a)
{
    typename T::size_type temp;
    for (temp = 0; temp != a.size(); temp++)
    {
        cout << a.at(temp) << ",";
    }
    cout << endl;
    return 0;
}

// forward declarations needed for friend declarations in Blob
template <typename>
class BlobPtr;
template <typename>
class Blob; // needed for parameters in operator==
template <typename>
class BlobUseSharedPtr;
template <typename T>
bool operator==(const Blob<T> &, const Blob<T> &);
template <typename T>
bool operator==(const BlobPtr<T> &, const BlobPtr<T> &);
template <typename T>
bool operator==(const BlobUseSharedPtr<T> &, const BlobUseSharedPtr<T> &);

template <typename T>
class Blob
{
    // each instantiation of Blob grants access to the version of
    // BlobPtr and the equality operator instantiated with the same type
    friend class BlobPtr<T>;
    friend bool operator== <T>(const Blob<T> &, const Blob<T> &);

public:
    typedef T value_type;
    typedef typename std::vector<T>::size_type size_type;

    // constructors
    Blob();
    Blob(std::initializer_list<T> il);
    template <typename It>
    Blob(It b, It e);

    // return BlobPtr to the first and one past the last elements
    BlobPtr<T> begin() { return BlobPtr<T>(*this); }
    BlobPtr<T> end()
    {
        auto ret = BlobPtr<T>(*this, data->size());
        return ret;
    }

    // number of elements in the Blob
    size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }

    // add and remove elements
    void push_back(const T &t) { data->push_back(t); }
    void push_back(T &&t) { data->push_back(std::move(t)); }
    void pop_back();

    template <typename... Args>
    inline void emplace_back(Args &&...args)
    {
        cout << "(" << __FUNCTION__ << ")"
             << " emplace_back(Args&&... args)\n";
        data->emplace_back(std::forward<Args>(args)...);
    }

    // element access
    T &back();
    T &operator[](size_type i);
    const T &operator[](size_type i) const;

private:
    std::shared_ptr<std::vector<T>> data;

    // throws msg if data[i] isn't valid
    void check(size_type i, const std::string &msg) const;
};

template <typename T>
class BlobPtr
{
    friend bool operator== <T>(const BlobPtr<T> &, const BlobPtr<T> &);

public:
    BlobPtr() : curr(0) {}
    BlobPtr(Blob<T> &a, size_t sz = 0) : wptr(a.data), curr(sz) {}

    T &operator[](std::size_t i)
    {
        auto p = check(i, "subscript out of range");
        return (*p)[i]; // (*p) is the vector to which this object points
    }

    T &operator*() const
    {
        auto p = check(curr, "dereference past end");
        return (*p)[curr]; // (*p) is the vector to which this object points
    }

    T *operator->() const
    { // delegate the real work to the dereference operator
        return &this->operator*();
    }

    // increment and decrement
    BlobPtr &operator++(); // prefix operators
    BlobPtr &operator--();

    // postfix increment and decrement
    BlobPtr operator++(int);
    BlobPtr operator--(int);

private:
    // check returns a shared_ptr to the vector if the check succeeds
    std::shared_ptr<std::vector<T>>
    check(std::size_t, const std::string &) const;

    // store a weak_ptr, which means the underlying vector might be destroyed
    std::weak_ptr<std::vector<T>> wptr;
    std::size_t curr; // current position within the array
};

template <typename T>
class SharedPtr;

template <typename T>
class BlobUseSharedPtr
{
    // each instantiation of Blob grants access to the version of
    // BlobPtr and the equality operator instantiated with the same type
    friend bool operator== <T>(const BlobUseSharedPtr<T> &, const BlobUseSharedPtr<T> &);

public:
    typedef T value_type;
    typedef typename std::vector<T>::size_type size_type;

    // constructors
    BlobUseSharedPtr();

    // number of elements in the Blob
    size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }

    // add and remove elements
    void push_back(const T &t) { data->push_back(t); }
    void push_back(T &&t) { data->push_back(std::move(t)); }
    void pop_back();

    // element access
    T &back();
    T &operator[](size_type i);
    const T &operator[](size_type i) const;

private:
    SharedPtr<std::vector<T>> data;

    // throws msg if data[i] isn't valid
    void check(size_type i, const std::string &msg) const;
};

template <pos H, pos W>
class ScreenT;

// class WindowMgrT
// {
// public:
//     WindowMgrT(pos h, pos w, char cts, size_t a);
//     void clear(size_t a);
//     void show(std::ostream &os) const;

// private:
//     vector<ScreenT> screens;
// };

template <pos H, pos W>
ostream &operator<<(ostream &os, const ScreenT<H, W> &ST);

template <pos H, pos W>
istream &operator>>(istream &is, ScreenT<H, W> &ST);

template <pos H, pos W>
class ScreenT
{
    // friend void Window_mgr::clear(size_t);
    friend ostream &operator<< <H, W>(ostream &os, const ScreenT<H, W> &ST);
    friend istream &operator>> <H, W>(istream &is, ScreenT<H, W> &ST);

public:
    ScreenT(pos c, char cts) : cursor(c), contents(H * W, cts) {}
    ScreenT(char cts) : ScreenT(0, cts) {}
    ScreenT() : ScreenT(0, '*') {}
    void show(std::ostream &os) const;
    inline pos size() const { return H * W; };
    ScreenT &move(pos row = 0, pos col = 0);
    ScreenT &set(pos row, pos col, char var);
    ScreenT &set(char var);

private:
    pos cursor = 0;
    std::string contents;
};

template <typename T>
class VecT;

template <typename T>
bool operator==(const VecT<T> &, const VecT<T> &);
template <typename T>
bool operator!=(const VecT<T> &, const VecT<T> &);

template <typename T>
class VecT
{
    friend bool operator== <T>(const VecT<T> &, const VecT<T> &);
    friend bool operator!= <T>(const VecT<T> &, const VecT<T> &);

public:
    // copy control members
    VecT() : elements(nullptr), first_free(nullptr), cap(nullptr) {}

    VecT(const VecT &);            // copy constructor
    VecT &operator=(const VecT &); // copy assignment
    VecT &operator=(std::initializer_list<T>);

    VecT(VecT &&) noexcept;            // move constructor
    VecT &operator=(VecT &&) noexcept; // move assignment

    ~VecT() noexcept; // destructor

    VecT(std::initializer_list<T>);

    void push_back(const T &); // copy the element
    void push_back(T &&);      // move the element

    // add elements
    size_t size() const { return first_free - elements; }
    size_t capacity() const { return cap - elements; }

    // iterator interface
    T *begin() const { return elements; }
    T *end() const { return first_free; }

    // [] function
    T &operator[](size_t i);

    // const [] function
    const T &operator[](size_t i) const;

    // at function
    T at(size_t i) const;

private:
    static std::allocator<T> alloc; // allocates the elements

    // utility functions:
    //  used by members that add elements to the VecT
    void chk_n_alloc()
    {
        if (size() == capacity())
            reallocate();
    }
    // used by the copy constructor, assignment operator, and destructor
    std::pair<T *, T *> alloc_n_copy(const T *, const T *);
    void free();       // destroy the elements and free the space
    void reallocate(); // get more space and copy the existing elements
    T *elements;       // pointer to the first element in the array
    T *first_free;     // pointer to the first free element in the array
    T *cap;            // pointer to one past the end of the array
};

// Exercises Section 16.1.1
// Exercise 16.1: Define instantiation.
// Answer: compiler generate target func using given arguments, this is called instantiation.
int exercise16_1();

template <typename T1, typename T2>
int compare(const T1 &v1, const T2 &v2)
{
    return (v1 < v2) ? 0 : 1;
}

// Exercise 16.2: Write and test your own versions of the compare functions.
int exercise16_2();

// Exercise 16.3: Call your compare function on two Sales_data objects to
// see how your compiler handles errors during instantiation.
int exercise16_3();

template <typename T1, typename T2>
T1 find_test(const T1 beg, const T1 end, const T2 &val)
{
    for (T1 start = beg; start != end; start++)
    {
        if (*start == val)
        {
            // *start = 100;
            return start;
        }
    }
    return end;
}

// Exercise 16.4: Write a template that acts like the library find algorithm.
// The function will need two template type parameters, one to represent the
// function’s iterator parameters and the other for the type of the value. Use
// your function to find a given value in a vector<int> and in a list<string>.
int exercise16_4();

template <typename T1, unsigned N>
void print(T1 (&p)[N])
{
    for (unsigned temp = 0; temp != N; temp++)
    {
        cout << p[temp] << ",";
    }
    cout << endl;
}

// Exercise 16.5: Write a template version of the print function from § 6.2.4
// (p. 217) that takes a reference to an array and can handle arrays of any size
// and any element type.
int exercise16_5();

template <typename T1, unsigned N>
T1 *begin(T1 (&p)[N])
{
    return p;
}

template <typename T1, unsigned N>
T1 *end(T1 (&p)[N])
{
    return p + N;
}

// Exercise 16.6: How do you think the library begin and end functions that
// take an array argument work? Define your own versions of these functions.
int exercise16_6();

template <typename T1, unsigned N>
constexpr unsigned sizeOfArray(T1 (&p)[N])
{
    return N;
}

// Exercise 16.7: Write a constexpr template that returns the size of a given
// array.
int exercise16_7();

// Exercise 16.8: In the “Key Concept” box on page 108, we noted that as a
// matter of habit C++ programmers prefer using != to using <. Explain the
// rationale for this habit.
// Answer: (1) many class or type does not define < operator.
// 避免越界：当使用 < 进行比较时，你需要知道容器的确切大小或者需要有一个明确的终止条件。
// 如果你不小心，可能会导致越界访问，尤其是当你错误地估计了容器的大小时。
// 而使用 != 与 end() 迭代器进行比较，则可以确保你不会越界，因为 end() 迭代器指向的是容器的“尾部之后”，是一个安全的哨兵值。
int exercise16_8();

// Exercises Section 16.1.2
// Exercise 16.9: What is a function template? What is a class template?
// Answer: A class template is a blueprint for generating classes. Class templates differ from
// function templates in that the compiler cannot deduce the template parameter type(s)
// for a class template.
int exercise16_9();

// Exercise 16.10: What happens when a class template is instantiated?
// Answer:
// when we use a class template, we must supply extra information.
// We can now see that that extra information is a list of explicit template
// arguments that are bound to the template’s parameters.
// when the compiler instantiates a class from a template, it rewrites the template ,
// replacing each instance of the template parameter T by the given template argument.
// The compiler generates a different class for each element type we specify.
int exercise16_10();

// Exercise 16.11: The following definition of List is incorrect. How would you fix it?
// Answer:
// template <typename elemType>
// class ListItem;
// template <typename elemType>
// class List
// {
// public:
//     List<elemType>();
//     List<elemType>(const List<elemType> &);
//     List<elemType> &operator=(const List<elemType> &);
//     ~List();
//     void insert(ListItem<elemType> *ptr, elemType value);

// private:
//     ListItem<elemType> *front, *end;
// };
int exercise16_11();

// Exercise 16.12: Write your own version of the Blob and BlobPtr
// templates. including the various const members that were not shown in the text.
int exercise16_12();

// Exercise 16.13: Explain which kind of friendship you chose for the equality
// and relational operators for BlobPtr.
int exercise16_13();

// Exercise 16.14: Write a Screen class template that uses nontype
// parameters to define the height and width of the Screen.
int exercise16_14();

// Exercise 16.15: Implement input and output operators for your Screen
// template. Which, if any, friends are necessary in class Screen to make the
// input and output operators work? Explain why each friend declaration, if any, was needed.
int exercise16_15();

// Exercise 16.16: Rewrite the StrVec class (§ 13.5, p. 526) as a template named Vec.
int exercise16_16();

// Exercises Section 16.1.3
// Exercise 16.17: What, if any, are the differences between a type parameter
// that is declared as a typename and one that is declared as a class? When must typename be used?
// Answer: Normally class and typename have no big difference, but if we want to use Class Members That Are Types（ for example: vector<T>::size_type）
int exercise16_17();

template <typename T, typename U, typename V>
void f1(T, U, V);

template <typename T>
T f2(T &);

template <typename T>
inline T foo(T, unsigned int *);

template <typename T>
void f4(T, T);

// typedef char Ctype; this line is meanless
template <typename Ctype>
Ctype f5(Ctype a);

// Exercise 16.18: Explain each of the following function template declarations
// and identify whether any are illegal. Correct each error that you find.
// (a) template <typename T, U, typename V> void f1(T, U, V);
// (b) template <typename T> T f2(int &T);
// (c) inline template <typename T> T foo(T, unsigned int*);
// (d) template <typename T> f4(T, T);
// (e) typedef char Ctype;
//     template <typename Ctype> Ctype f5(Ctype a);
int exercise16_18();

// Exercise 16.19: Write a function that takes a reference to a container and
// prints the elements in that container. Use the container’s size_type and
// size members to control the loop that prints the elements.
int exercise16_19();

// Exercise 16.20: Rewrite the function from the previous exercise to use
// iterators returned from begin and end to control the loop.
int exercise16_20();

// Exercises Section 16.1.4
// Exercise 16.21: Write your own version of DebugDelete.
int exercise16_21();

// Exercise 16.22: Revise your TextQuery programs from § 12.3 (p. 484) so
// that the shared_ptr members use a DebugDelete as their deleter (§
// 12.1.4, p. 468).
// Answer: see exercise12_34
int exercise16_22();

// Exercise 16.23: Predict when the call operator will be executed in your
// main query program. If your expectations and what happens differ, be sure
// you understand why.
int exercise16_23();

// Exercise 16.24: Add a constructor that takes two iterators to your Blob template.
int exercise16_24();

// Exercises Section 16.1.5
// Exercise 16.25: Explain the meaning of these declarations:
// extern template class vector<string>;  instantiation declaration 实例化声明
// template class vector<Sales_data>;      instantiation definition 实例化定义
int exercise16_25();

// Exercise 16.26: Assuming NoDefault is a class that does not have a
// default constructor, can we explicitly instantiate vector<NoDefault>? If not, why not?
// Answer: No. When we explicitly instantiate vector<NoDefault>, the compiler will instantiate
// all member funcs of vector<NoDefault>. However, NoDefault does not have default constructor,
// then the compiler CANNOT generate default constructor for vector<NoDefault>. Therefore, we CANNOT
// explicitly instantiate vector<NoDefault>.
int exercise16_26();

// Exercise 16.27: For each labeled statement explain what, if any,
// instantiations happen. If a template is instantiated, explain why; if not,
// explain why not.
template <typename T>
class Stack
{
public:
    Stack() { cout << "instantiation happened.\n"; }

private:
    T temp;
};

void f1(Stack<char>); // (a)  No instantiation happens here. This is just a function declaration that takes a Stack<char> by value as its parameter. No code is executed that would cause the template to be instantiated into a specific type.

class Exercise_chapter16
{
    Stack<double> &rsd; // (b) Yes, an instantiation of Stack occurs for the type double.
    Stack<int> si;      // (c) Yes, an instantiation of Stack occurs for the type int.
};

// int main()
// {
//     Stack<char> *sc;                  // (d) Yes, an instantiation of Stack occurs for the type char.
//     f1(*sc);                          // (e) Yes, copy *sc to f1's parameter
//     int iObj = sizeof(Stack<string>); // (f) Yes, an instantiation of Stack occurs for the type std::string.
// }
int exercise16_27();

template <typename T>
class SharedPtr
{
public:
    explicit SharedPtr(T *ptr = nullptr, std::function<void(T *)> deleter = std::default_delete<T>())
        : s_ptr(ptr), ptr_deleter(deleter), s_cnt(new std::size_t(1))
    {
    }

    SharedPtr(const SharedPtr &other)
        : s_ptr(other.s_ptr), ptr_deleter(other.ptr_deleter), s_cnt(other.s_cnt)
    {
        ++*s_cnt;
    }

    ~SharedPtr()
    {
        --*s_cnt;
        if (*s_cnt == 0)
        {
            ptr_deleter(s_ptr);
        }
    }

    void swap(SharedPtr &other) noexcept
    {
        std::swap(s_ptr, other.s_ptr);
        std::swap(ptr_deleter, other.ptr_deleter);
        std::swap(s_cnt, other.s_cnt);
    }

    SharedPtr &operator=(const SharedPtr &other)
    {
        if (this != &other)
        {
            // Because other is const object, no way to call non-const func swap,
            // thereforew, we use temp object SharedPtr(other) to call swap.
            // when this block is execuated, SharedPtr(other) will be destoryed. So, this way works.
            SharedPtr(other).swap(*this);
        }
        return *this;
    }

    void reset(T *ptr = nullptr, std::function<void(T *)> deleter = std::default_delete<T>())
    {
        SharedPtr(ptr, deleter).swap(*this);
    }

    std::size_t useCount() const
    {
        return *s_cnt;
    }

    T *get() const
    {
        return s_ptr;
    }

    T *operator->() const
    {
        return this->get();
    }

    T &operator*()
    {
        if (s_ptr != nullptr)
        {
            return *s_ptr;
        }
        else
            throw std::runtime_error("dereferencing nullptr.");
    }

    const T &operator*() const
    {
        if (s_ptr != nullptr)
        {
            return *s_ptr;
        }
        else
            throw std::runtime_error("dereferencing nullptr.");
    }

private:
    T *s_ptr;
    std::size_t *s_cnt;
    std::function<void(T *)> ptr_deleter;
};

template <typename T, typename D = std::function<void(T *)>>
class UniquePtr
{
public:
    explicit UniquePtr(T *ptr = nullptr, D deleter = std::default_delete<T>())
        : s_ptr(ptr), ptr_deleter(deleter)
    {
    }

    UniquePtr(const UniquePtr &other) = delete;

    ~UniquePtr()
    {
        ptr_deleter(s_ptr);
    }

    UniquePtr &operator=(const UniquePtr &other) = delete;

    void reset(T *ptr = nullptr)
    {
        ptr_deleter(s_ptr);
        s_ptr = ptr;
    }

    T *release()
    {
        T *ptr = s_ptr;
        s_ptr = nullptr;
        return ptr;
    }

    T operator*()
    {
        if (s_ptr != nullptr)
        {
            return *s_ptr;
        }
        else
            throw std::runtime_error("dereferencing nullptr.");
    }

private:
    T *s_ptr;
    D ptr_deleter;
};

// Exercises Section 16.1.6
// Exercise 16.28: Write your own versions of shared_ptr and unique_ptr.
int exercise16_28();

// Exercise 16.29: Revise your Blob class to use your version of
// shared_ptr rather than the library version.
int exercise16_29();

// Exercise 16.30: Rerun some of your programs to verify your shared_ptr
// and revised Blob classes. (Note: Implementing the weak_ptr type is
// beyond the scope of this Primer, so you will not be able to use the BlobPtr
// class with your revised Blob.)
// Answer: see exercise16_29
int exercise16_30();

// Exercise 16.31: Explain how the compiler might inline the call to the deleter
// if we used DebugDelete with unique_ptr.
// Answer: DebugDelete is relatively short and stright-forward.
int exercise16_31();

// Exercises Section 16.2.1
// Exercise 16.32: What happens during template argument deduction?
// Answer: During template argument deduction, the compiler uses types of the
// arguments in the call to find the template arguments that generate a version of the
// function(having parameters) that best matches the given call.
int exercise16_32();

// Exercise 16.33: Name two type conversions allowed on function arguments
// involved in template argument deduction.
// Answer: (1) const conversions; (2) Array- or function-to-pointer conversions:
int exercise16_33();

template <class T>
inline int compareWrong(const T &a, const T &b)
{
    return (a == b) ? 100 : 200;
}

// Exercise 16.34: Given only the following code, explain whether each of
// these calls is legal. If so, what is the type of T? If not, why not?
// template <class T> int compare(const T&, const T&);
// (a) compare("hi", "world");
// (b) compare("bye", "dad");
int exercise16_34();

template <typename T>
inline T calc(T a, int b)
{
    return a + b;
}

template <typename T>
inline T fcn(T a, T b)
{
    return a - b;
}

// Exercise 16.35: Which, if any, of the following calls are errors? If the call is
// legal, what is the type of T? If the call is not legal, what is the problem?
// template <typename T> T calc(T, int);
// template <typename T> T fcn(T, T);
// double d; float f; char c;
// (a) calc(c, 'c');    legal
// (b) calc(d, f);      legal
// (c) fcn(c, 'c');     legal
// (d) fcn(d, f);       illegal
int exercise16_35();

template <typename T>
inline void f1(T a, T b)
{
    cout << "1" << endl;
}

template <typename T1, typename T2>
inline void f2(T1 a, T2 b)
{
    cout << "2" << endl;
}

// Exercise 16.36: What happens in the following calls:
// template <typename T> f1(T, T);
// template <typename T1, typename T2) f2(T1, T2);
// int i = 0, j = 42, *p1 = &i, *p2 = &j;
// const int *cp1 = &i, *cp2 = &j;
// (a) f1(p1, p2);
// (b) f2(p1, p2);
// (c) f1(cp1, cp2);
// (d) f2(cp1, cp2);
// (e) f1(p1, cp1);
// (f) f2(p1, cp1);
int exercise16_36();

// Exercises Section 16.2.2
// Exercise 16.37: The library max function has two function parameters and
// returns the larger of its arguments. This function has one template type
// parameter. Could you call max passing it an int and a double? If so, how?
// If not, why not?
int exercise16_37();

// Exercise 16.38: When we call make_shared (§ 12.1.1, p. 451), we have to
// provide an explicit template argument. Explain why that argument is needed
// and how it is used.
// Answer: explicit template argument is used to init the return type of make_shared.
int exercise16_38();

// Exercise 16.39: Use an explicit template argument to make it sensible to
// pass two string literals to the original version of compare from § 16.1.1 (p.
// 652).
int exercise16_39();

template <typename It>
auto fcn3(It beg, It end) -> decltype(*beg + 0)
{
    // process the range
    return *beg; // return a copy of an element from the range
}

// Exercises Section 16.2.3
// Exercise 16.40: Is the following function legal? If not, why not? If it is
// legal, what, if any, are the restrictions on the argument type(s) that can be
// passed, and what is the return type?
// template <typename It>
// auto fcn3(It beg, It end) -> decltype(*beg + 0)
// {
//  // process the range
//  return *beg; // return a copy of an element from the range
// }
// Answer: when *beg is of type int or sth has operator + with 0, this func works fine.
int exercise16_40();

template <typename T = long long>
inline auto sumTest(T a, T b) -> typename std::make_unsigned<decltype(a)>::type
{
    return a + b;
}

// Exercise 16.41: Write a version of sum with a return type that is
// guaranteed to be large enough to hold the result of the addition.
int exercise16_41();

// Exercises Section 16.2.5
// Exercise 16.42: Determine the type of T and of val in each of the
// following calls:
// template <typename T> void g(T&& val);
// int i = 0; const int ci = i;
// (a) g(i);        T: int & ; val: "int & &&" collapse to "int&"
// (b) g(ci);       T: const int & ; val: "const int & &&" collapse to "const int&"
// (c) g(i * ci);   T: int ; val: int &&
int exercise16_42();

// Exercise 16.43: Using the function defined in the previous exercise, what
// would the template parameter of g be if we called g(i = ci)?
// Answer: 在C++中，赋值表达式的值是赋值后左侧变量的值，是一个右值。所以 g(i = ci)中， T 是 int; val 是 int &&
int exercise16_43();

// Exercise 16.44: Using the same three calls as in the first exercise,
// determine the types for T if g’s function parameter is declared as T (not
// T&&). What if g’s function parameter is const T&?
// Answer: WHEN: template <typename T> void g(T val);
// int i = 0; const int ci = i;
// (a) g(i);        T: int ; val: "int"
// (b) g(ci);       T: const int ; val: "const int"
// (c) g(i * ci);   T: int && ; val: int &&
// WHEN: template <typename T> void g(const T & val);
// int i = 0; const int ci = i;
// (a) g(i);        T: int ; val: "const int &"
// (b) g(ci);       T: int ; val: "const int &"
// (c) g(i * ci);   T: int ; val: "const int &"
int exercise16_44();

// Exercise 16.45: Given the following template, explain what happens if we
// call g on a literal value such as 42. What if we call g on a variable of type
// int?
// template <typename T> void g(T&& val) { vector<T> v; }
int exercise16_45();

// Exercises Section 16.2.6
// Exercise 16.46: Explain this loop from VecT::reallocate in § 13.5 (p. 530):
// for (size_t i = 0; i != size(); ++i)
//     alloc.construct(dest++, std::move(*elem++));
// Answer: see chapter13.cpp's "void StrVec::reallocate()"
int exercise16_46();

template <typename F, typename T1, typename T2>
inline void flip(F f, T1 &&a, T2 &&b)
{
    // forward返回显式实参类型的右值引用
    f(std::forward<T2>(b), std::forward<T1>(a));
    // f(b,a);
}

// Exercises Section 16.2.7
// Exercise 16.47: Write your own version of the flip function and test it by
// calling functions that have lvalue and rvalue reference parameters.
int exercise16_47();

template <typename T>
inline void debug_rep(T *a)
{
    cout << "T * a: " << a << endl;
}

template <typename T>
inline void debug_rep(const T &a)
{
    cout << "const T & a: " << a << endl;
}

inline void debug_rep(const string &a)
{
    cout << "const string & a: " << a << endl;
}

// Exercises Section 16.3
// Exercise 16.48: Write your own versions of the debug_rep functions.
int exercise16_48();

template <typename T>
inline void f(T t)
{
    cout << "f(T)" << t << endl;
}
template <typename T>
inline void f(T *t)
{
    cout << "f(T *)" << *t << endl;
}
template <typename T>
inline void f(const T *t)
{
    cout << "f(const T *)" << *t << endl;
}
template <typename T>
inline void g(T t)
{
    cout << "g(T)" << t << endl;
}
template <typename T>
inline void g(T *t)
{
    cout << "g(T *)" << *t << endl;
}

// Exercise 16.49: Explain what happens in each of the following calls:
// template <typename T> void f(T);
// template <typename T> void f(const T*);
// template <typename T> void g(T);
// template <typename T> void g(T*);
// int i = 42, *p = &i;
// const int ci = 0, *p2 = &ci;
// g(42); g(p); g(ci); g(p2);
// f(42); f(p); f(ci); f(p2);
int exercise16_49();

// Exercise 16.50: Define the functions from the previous exercise so that they
// print an identifying message. Run the code from that exercise. If the calls
// behave differently from what you expected, make sure you understand why.
int exercise16_50();

template <typename T, typename... Args>
void foo(const T &t, const Args &...rest)
{
    cout << "sizeof ...(Args): " << sizeof...(Args) << endl;
    cout << "sizeof ...(rest): " << sizeof...(rest) << endl;
}

// Exercises Section 16.4
// Exercise 16.51: Determine what sizeof...(Args) and
// sizeof...(rest) return for each call to foo in this section.
int exercise16_51();

// Exercise 16.52: Write a program to check your answer to the previous question.
// see exercise16_51
int exercise16_52();

template <typename T>
ostream &print(ostream &os, const T &t)
{
    return os << t;
}

template <typename T, typename... Args>
ostream &print(ostream &os, const T &t, const Args &...rest)
{
    os << t << ", ";
    return print(os, rest...);
}

// Exercises Section 16.4.1
// Exercise 16.53: Write your own version of the print functions and test
// them by printing one, two, and five arguments, each of which should have
// different types.
int exercise16_53();

// Exercise 16.54: What happens if we call print on a type that doesn’t have
// an << operator?
int exercise16_54();

// Exercise 16.55: Explain how the variadic version of print would execute if
// we declared the nonvariadic version of print after the definition of the
// variadic version.
// Answer: As it cannot find the the definition of the nonvariadic version,
// the variadic version will match itself.
// Unlike an ordinary argument, a parameter pack
// can be empty. Hence, the variadic version of print can be instantiated with only two
// parameters: one for the ostream& parameter and the other for the const T&
// parameter.
int exercise16_55();

template <typename T>
inline const T &rep(const T &a)
{
    cout << "const T & a: " << a << endl;
    return a;
}

template <typename... Args>
ostream &errorMsg(ostream &os, const Args &...rest)
{
    return print(os, rep(rest)...);
}

// Exercises Section 16.4.2
// Exercise 16.56: Write and test a variadic version of errorMsg.
int exercise16_56();

// Exercise 16.57: Compare your variadic version of errorMsg to the
// error_msg function in § 6.2.6 (p. 220). What are the advantages and
// disadvantages of each approach?
// Answer: variadic version has more advantages, but it takes more risk as we dont know the real type.
int exercise16_57();

// Exercises Section 16.4.3
// Exercise 16.58: Write the emplace_back function for your StrVec class
// and for the Vec class that you wrote for the exercises in § 16.1.2 (p. 668).
int exercise16_58();

// Exercise 16.59: Assuming s is a string, explain svec.emplace_back(s).
int exercise16_59();

// Exercise 16.60: Explain how make_shared (§ 12.1.1, p. 451) works.
int exercise16_60();

template <typename T, typename... Args>
std::shared_ptr<T> make_shared_test(Args &&...args)
{
    // string * temp;
    // std::allocator<std::string> alloc; // allocates the elements
    // alloc.construct(temp, std::forward<Args>(args)...);
    // return std::shared_ptr<string>(temp);

    return std::shared_ptr<T>(new T(std::forward<Args>(args)...));
}

// Exercise 16.61: Define your own version of make_shared.
int exercise16_61();

namespace std
{
    template <>             // we're defining a specialization with
    struct hash<Sales_data> // the template parameter of Sales_data
    {
        // the type used to hash an unordered container must define these types
        typedef size_t result_type;
        typedef Sales_data argument_type; // by default, this type needs ==

        size_t operator()(const Sales_data &s) const;
        // our class uses synthesized copy control and default constructor
    };
    inline size_t hash<Sales_data>::operator()(const Sales_data &s) const
    {
        return hash<string>()(s.bookNo) ^ hash<unsigned>()(s.units_sold) ^ hash<double>()(s.revenue);
    }
}

// Exercises Section 16.5
// Exercise 16.62: Define your own version of hash<Sales_data> and
// define an unordered_multiset of Sales_data objects. Put several
// transactions into the container and print its contents.
int exercise16_62();

// Exercise 16.63: Define a function template to count the number of
// occurrences of a given value in a vector. Test your program by passing it a
// vector of doubles, a vector of ints, and a vector of strings.
// Answer: give up for now, will do it if I need this piece of knowledge in the future.
int exercise16_63();

// Exercise 16.64: Write a specialized version of the template from the
// previous exercise to handle vector<const char*> and a program that
// uses this specialization.
// Answer: give up for now, will do it if I need this piece of knowledge in the future.
int exercise16_64();

// Exercise 16.65: In § 16.3 (p. 698) we defined overloaded two versions of
// debug_rep one had a const char* and the other a char* parameter.
// Rewrite these functions as specializations.
// Answer: give up for now, will do it if I need this piece of knowledge in the future.
int exercise16_65();

// Exercise 16.66: What are the advantages and disadvantages of overloading
// these debug_rep functions as compared to defining specializations?
// Answer: give up for now, will do it if I need this piece of knowledge in the future.
int exercise16_66();

// Exercise 16.67: Would defining these specializations affect function
// matching for debug_rep? If so, how? If not, why not?
// Answer: give up for now, will do it if I need this piece of knowledge in the future.
int exercise16_67();

#endif